/**
 * @file main.c
 * @brief entry for the elgalso program
 * @author Scott Moyers
 * @date 2008
 */

#include "dot_writer.h"
#include "module.h"
#include "population.h"
#include "dSFMT.h"
#include "random_module_generator.h"
#include "config.h"
#include "util.h"

#include <math.h>
#include <time.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <limits.h>

/*#define SEED (getpid() * time(0))*/
#define SEED (1234567)

extern SelectionFunc selection_functions[SEL_FUNCS];
extern MutationFunc mutation_functions[MUT_FUNCS];
extern CrossoverFunc crossover_functions[CROSS_FUNCS];

int test_dot(const char * filename, struct Module *m)
{
	FILE *fp;
	if (!(fp = fopen(filename, "w"))) {
		printf("Can't open %s for writing\n", filename);
		return 1;
	}
	print_module_dot(fp, m, SIGNALS);
	fclose(fp);
	return 0;
}

int test_rand_module(unsigned int n, unsigned int nins, unsigned int ngates)
{
	dsfmt_t dsfmt;
	unsigned int i;
	int d_sum = 0;
	int g_sum = 0;
	struct Module **m;

	dsfmt_init_gen_rand(&dsfmt, SEED);
	m = (struct Module **) smalloc(sizeof(struct Module *) * n);
	for(i = 0; i < n; i++) {
		char filename[128];
		sprintf(filename, "%d.dot", i);
		m[i] = rand_module(&dsfmt, i, nins, ngates);
		test_dot(filename, m[i]);
		depth_module(m[i]);
		execute_module(m[i]);
		/*fprint_module(stdout, m[i]);*/
	}

	/* print some statistics about the modules */
	for(i = 0; i < n; i++) {
		printf("%d: gates = %d\n", i, m[i]->num_gates);
		g_sum += m[i]->num_gates;

	}
	printf("\n");
	for(i = 0; i < n; i++) {
		unsigned int d = depth_module(m[i]);
		d_sum += d;
		printf("%d: depth = %d\n", i, d);
	}
	printf("\n");
	printf("av. gates = %d\n", g_sum / n);
	printf("av. depth = %d\n", d_sum / n);

	for (i = 0; i < n; i++) {
		free_module(m[i]);
	}
	free(m);
	return 0;
}

void test_random_population(unsigned int inputs, unsigned int outputs,
			    unsigned int max_gates,
			    unsigned int size, unsigned int gens)
{
	unsigned int i, max = 0;
	dsfmt_t dsfmt;
	struct Population *p;
	dsfmt_init_gen_rand(&dsfmt, SEED);
	p = new_population();
	p->truth_table = new_rand_truth_table(&dsfmt, inputs, outputs);
	print_truth_table(stdout, p->truth_table);
	printf("\n");
	p->max_ckt_size = max_gates;
	add_selection_func(p, fittest_selection);
	add_selection_func(p, roulette_tournament_selection);
	add_mutation_funcs(p, mutation_functions, MUT_FUNCS);
	add_crossover_funcs(p, crossover_functions, CROSS_FUNCS);
	generate_population(p, &dsfmt, size);
	evaluate_population(p);
	for(i = 0; i < gens; i++) {
		double fit = get_fittest(p)->fitness;
		printf("%d: fittest: %g average: %g\n",
		       p->generation, fit , average_fitness(p));
		next_generation(p, &dsfmt);

		if (fit > max) {
			FILE *ostream;
			max = fit;
			ostream = fopen("fittest.dot", "w");
			print_module_dot(ostream, get_fittest(p), 0);
			fclose(ostream);
		}
	}

	delete_population(p);
}

void test_population(const char * fn, unsigned int max_gates,
		     unsigned int size, unsigned int gens)
{
	unsigned int i, max = 0;
	dsfmt_t dsfmt;
	struct Population *p;
	dsfmt_init_gen_rand(&dsfmt, SEED);
	p = new_population();
	p->truth_table = new_truth_table_from_file(fn);
	print_truth_table(stdout, p->truth_table);
	printf("\n");
	p->max_ckt_size = max_gates;
	add_selection_func(p, fittest_selection);
	add_selection_func(p, roulette_tournament_selection);
	add_mutation_funcs(p, mutation_functions, MUT_FUNCS);
	add_crossover_funcs(p, crossover_functions, CROSS_FUNCS);
	generate_population(p, &dsfmt, size);
	evaluate_population(p);

	for(i = 0; i < gens; i++) {
		double fit = get_fittest(p)->fitness;
		printf("%d: fittest: %g average: %g\n",
		       p->generation, fit , average_fitness(p));
		next_generation(p, &dsfmt);

		if (fit > max) {
			FILE *ostream;
			max = fit;
			ostream = fopen("fittest.dot", "w");
			print_module_dot(ostream, get_fittest(p), 0);
			fclose(ostream);
		}
	}

	delete_population(p);
}

void test_roulette(unsigned int iterations, unsigned int n)
{
	struct Module **m = (struct Module **) smalloc(sizeof(struct Module *) * n);
	struct Module **m_cpy = (struct Module **) smalloc(sizeof(struct Module *) * n);
	unsigned int *freq = (unsigned int *) scalloc(n, sizeof(unsigned int));
	double prob = 0.1;
	double prob_sum = 0.0;
	unsigned int i, j;
	dsfmt_t dsfmt;
	dsfmt_init_gen_rand(&dsfmt, SEED);
	for(i = 0; i < n; i++) {
		m[i] = new_module(i, 1, 1);
		m[i]->fitness = prob;
		prob_sum += prob;
		prob *= 2.0;
	}

	for(j = 0; j < iterations; j++) {
		for(i = 0; i < n; i++) {
			m_cpy[i] = copy_module(m[i]);
		}

		roulette_wheel_selection(&dsfmt, m_cpy, n, n);

		for(i = 0; i < n; i++) {
			unsigned int k;
			for(k = 0; k < n; k++) {
				if (m_cpy[i]->id <= (signed)k) {
					freq[k]++;
					break;
				}
			}
		}

		for(i = 0; i < n; i++) {
			free_module(m_cpy[i]);
		}
	}

	printf("probabilities:");
	for(i = 0; i < n; i++) {
		printf(" %.2f", m[i]->fitness);
	}
	printf("\n");

	printf("frequency:");
	for(i = 0; i < n; i++) {
		printf(" %.2f", prob_sum * ((double)freq[i] / (iterations * n)));
	}
	printf("\n");

	for(i = 0; i < n; i++) {
		free_module(m[i]);
	}
	free(m);
	free(m_cpy);
	free(freq);
}

void test_config_file(const char *filename)
{
	unsigned int i, max = 0;
	dsfmt_t dsfmt;
	struct Population *p;
	dsfmt_init_gen_rand(&dsfmt, SEED);
	p = new_population();
	if(load_config_file(p, filename) != 0) {
		return;
	}

	generate_population(p, &dsfmt, p->size);
	evaluate_population(p);
	for(i = 0; i < p->max_generations; i++) {
		double fit = get_fittest(p)->fitness;
		printf("%d: fittest: %g average: %g\n",
		       p->generation, fit , average_fitness(p));
		fflush(stdout);
		next_generation(p, &dsfmt);

		if (fit > max) {
			FILE *ostream;
			max = fit;
			ostream = fopen("fittest.dot", "w");
			print_module_dot(ostream, get_fittest(p), 0);
			fclose(ostream);
		}
	}

	delete_population(p);
}

int test_layout(unsigned int n, unsigned int nins, unsigned int ngates)
{
	dsfmt_t dsfmt;
	unsigned int i;
	struct Module **m;

	dsfmt_init_gen_rand(&dsfmt, SEED);
	m = (struct Module **) smalloc(sizeof(struct Module *) * n);
	for(i = 0; i < n; i++) {
		unsigned int j;
		char filename[128];
		sprintf(filename, "%d.dot", i);
		m[i] = rand_module(&dsfmt, i, nins, ngates);
		test_dot(filename, m[i]);
		layout_module(m[i]);
		for(j = 0; j < m[i]->num_gates; j++) {
			struct Gate *g = &m[i]->circuit[j];
			printf("depth:%d x:%f y:%f\n", g->level, g->x, g->y);
		}
	}

	for (i = 0; i < n; i++) {
		free_module(m[i]);
	}
	free(m);
	return 0;
}

void usage(int argc, char *argv[])
{
	(void) argc;
	printf("Usage:\n");
	printf("\t%s config_file.cfg\n", argv[0]);
	exit(1);
}

int main(int argc, char *argv[])
{
	/*test_rand_module(10, 4, 100);*/
	/*test_random_population(5, 1, 50, 1000, 10000);*/
	/*test_roulette(1000000, 3); */
	/*test_population("./truths/adder4.truth", 90, 1000, 100000); */
	/*test_layout(1, 4, 10);*/
	if (argc != 2) {
		usage(argc, argv);
	}

	test_config_file(argv[1]);
	return 0;
}
